/* create.c : create a process
 */

#include <kernel.h>

static unsigned int pid_counter;	/* Used to assign unique process ID's */

/* Simple function to initialize pid_counter to constant defined in kernel.h */
extern void pidcounterinit(void){
	pid_counter = PID_COUNT_START;
}

/*
*	Takes a pointer to a process' entry point, and an int for how large to make its stack.	
*	Gets a PCB if one exists (see disp.c for get_pcb()), returns 0 if none available.
*	Creates a process, sets up its stack and context, and places it onto the ready queue
*	Finally, returns the pid of this process
*/

extern int create( void (*fn)(), int stack){
	PCB* proc_pcb = get_pcb();
	return __create(fn, stack, proc_pcb, TRUE);
}

extern int __create( void (*fn)(), int stack, PCB* proc_pcb, int b_ready){	
	
	//kprintf("****Attempting to create process...\n");
	
	if(!proc_pcb){
		kprintf("Unable to create new proccess: no available PCB's\n");
		return 0;
	}
	if(stack < sizeof(CPU_context)+CPPADDING) stack = sizeof(CPU_context)+CPPADDING;
	void* membase = kmalloc(stack);
	CPU_context* ctxt = (((int)membase)+stack-CPPADDING-sizeof(CPU_context)-4); //position context            	
	int bp = (int)ctxt+sizeof(CPU_context);	
	int* positioner = (int*)bp;
	*positioner = &sysstop;

	ctxt->edi = 0;
	ctxt->esi = 0;
	ctxt->ebp = bp;
	ctxt->esp = bp;
	ctxt->ebx = 0;
	ctxt->edx = 0;
	ctxt->ecx = 0;
	ctxt->eax = 0;
	ctxt->iret_eip = (int)fn;
	ctxt->iret_cs = getCS();
	ctxt->eflags = 0x00003200; // to enable interrupts initially
	
	proc_pcb->pid = pid_counter++;
	if(pid_counter == 0){
		kprintf("PID counter has wrapped around, see create.c\n");
		pid_counter = PID_COUNT_START;
	}
	proc_pcb->state = PROC_READY;
	proc_pcb->cpu_state = ctxt;
	proc_pcb->stack = (void*)ctxt;
	proc_pcb->initial_stack = (void*)membase;
	
	if(b_ready){
		//kprintf("****Created process %d, adding to ready queue.\n", proc_pcb->pid);
		ready(proc_pcb);
	}
	
	//kprintf("****Finished process %d creation.\n", proc_pcb->pid); 
	
	return proc_pcb->pid;
}

